﻿using System;
using System.Globalization;
using System.Xml;

namespace OXM
{
    public class StringConverter : ISimpleTypeConverter<string>
    {
        public string ToXml(string value)
        {
            return value;
        }

        public string ToObj(string value)
        {
            return value;
        }
    }

    public class BooleanConverter : ISimpleTypeConverter<bool>
    {
        public string ToXml(bool value)
        {
            return XmlConvert.ToString(value);
        }

        public bool ToObj(string value)
        {
            return XmlConvert.ToBoolean(value);
        }
    }

    public class NullableBooleanConverter : ISimpleTypeConverter<bool?>
    {
        readonly BooleanConverter _converter = new BooleanConverter();
        
        public string ToXml(bool? value)
        {
            return !value.HasValue ? null : _converter.ToXml(value.Value);
        }

        public bool? ToObj(string value)
        {
            if (value == null)
                return null;

            return _converter.ToObj(value);
        }
    }

    public class Int32Converter : ISimpleTypeConverter<int>
    {
        public string ToXml(int value)
        {
            return XmlConvert.ToString(value);
        }

        public int ToObj(string value)
        {
            return XmlConvert.ToInt32(value);
        }
    }

    public class NullableInt32Converter : ISimpleTypeConverter<int?>
    {
        readonly Int32Converter _converter = new Int32Converter();

        public string ToXml(int? value)
        {
            return !value.HasValue ? null : _converter.ToXml(value.Value);
        }

        public int? ToObj(string value)
        {
            if (value == null)
                return null;

            return _converter.ToObj(value);
        }
    }

    public class UriConverter : ISimpleTypeConverter<Uri>
    {
        public string ToXml(Uri value)
        {
            return value == null ? null : value.ToString();
        }

        public Uri ToObj(string value)
        {
            return new Uri(value);
        }
    }

    public class EnumConverter<T> : ISimpleTypeConverter<T>
    {
        public string ToXml(T value)
        {
            return value == null ? null : value.ToString();
        }

        public T ToObj(string value)        
        {
            var enumType = typeof(T);
            var genericTypeDefinition = enumType.GetGenericTypeDefinition();
            if (genericTypeDefinition != null && (enumType.IsGenericType && genericTypeDefinition == typeof(Nullable<>)))
            {
                var nonNullableType = Nullable.GetUnderlyingType(enumType);
                return (T)Enum.Parse(nonNullableType, value);
            }
            return (T)Enum.Parse(typeof(T), value);
        }
    }

    public class DateTimeConverter : ISimpleTypeConverter<DateTime>
    {
        public string ToXml(DateTime value)
        {
            return value.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssK");
        }

        public DateTime ToObj(string value)
        {
            return XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.RoundtripKind);
        }
    }

    public class NullableDateTimeConverter : ISimpleTypeConverter<DateTime?>
    {
        public string ToXml(DateTime? value)
        {
            return !value.HasValue ? null : value.Value.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssK");
        }

        public DateTime? ToObj(string value)
        {
            if (value == null)
                return null;

            return XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.RoundtripKind);
        }
    }

    public class DecimalConverter : ISimpleTypeConverter<decimal>
    {
        public string ToXml(decimal value)
        {
            return value.ToString(CultureInfo.InvariantCulture);
        }

        public decimal ToObj(string value)
        {
            return XmlConvert.ToDecimal(value);
        }
    }

    public class NullableDecimalConverter : ISimpleTypeConverter<decimal?>
    {
        readonly DecimalConverter _converter = new DecimalConverter();

        public string ToXml(decimal? value)
        {
            return !value.HasValue ? null : _converter.ToXml(value.Value);
        }

        public decimal? ToObj(string value)
        {
            if (value == null)
                return null;
            return _converter.ToObj(value);
        }
    }

    public class DateTimeOffsetConverter : ISimpleTypeConverter<DateTimeOffset>
    {
        public string ToXml(DateTimeOffset value)
        {
            return value.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssK");
        }

        public string ToXml(DateTimeOffset value, string format)
        {
            return value.ToString(format);
        }

        public DateTimeOffset ToObj(string value)
        {
            return XmlConvert.ToDateTimeOffset(value);
        }
    }

    public class NullableDateTimeOffsetConverter : ISimpleTypeConverter<DateTimeOffset?>
    {
        public string ToXml(DateTimeOffset? value)
        {
            return !value.HasValue ? null : value.Value.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssK");
        }

        public DateTimeOffset? ToObj(string value)
        {
            if (value == null)
                return null;

            return XmlConvert.ToDateTimeOffset(value);
        }
    }
}
